home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 15 / CU Amiga Magazine's Super CD-ROM 15 (1997)(EMAP Images)(GB)[!][issue 1997-10].iso / CUCD / Graphics / Ghostscript / source / zchar.c < prev    next >
C/C++ Source or Header  |  1997-04-09  |  18KB  |  648 lines

  1. /* Copyright (C) 1989, 1996, 1997 Aladdin Enterprises.  All rights reserved.
  2.   
  3.   This file is part of Aladdin Ghostscript.
  4.   
  5.   Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author
  6.   or distributor accepts any responsibility for the consequences of using it,
  7.   or for whether it serves any particular purpose or works at all, unless he
  8.   or she says so in writing.  Refer to the Aladdin Ghostscript Free Public
  9.   License (the "License") for full details.
  10.   
  11.   Every copy of Aladdin Ghostscript must include a copy of the License,
  12.   normally in a plain ASCII text file named PUBLIC.  The License grants you
  13.   the right to copy, modify and redistribute Aladdin Ghostscript, but only
  14.   under certain conditions described in the License.  Among other things, the
  15.   License requires that the copyright notice and this notice be preserved on
  16.   all copies.
  17. */
  18.  
  19. /* zchar.c */
  20. /* Character operators */
  21. #include "ghost.h"
  22. #include "errors.h"
  23. #include "oper.h"
  24. #include "gsstruct.h"
  25. #include "gxarith.h"
  26. #include "gxfixed.h"
  27. #include "gxmatrix.h"            /* for ifont.h */
  28. #include "gxchar.h"            /* for stringwidth_flag */
  29. #include "gxdevice.h"            /* for gxfont.h */
  30. #include "gxfont.h"
  31. #include "gzpath.h"
  32. #include "gzstate.h"
  33. #include "dstack.h"            /* for stack depth */
  34. #include "estack.h"
  35. #include "ialloc.h"
  36. #include "ichar.h"
  37. #include "idict.h"
  38. #include "ifont.h"
  39. #include "igstate.h"
  40. #include "ilevel.h"
  41. #include "iname.h"
  42. #include "ipacked.h"
  43. #include "store.h"
  44.  
  45. /* Forward references */
  46. private bool map_glyph_to_char(P3(const ref *, const ref *, ref *));
  47. private int finish_show(P1(os_ptr));
  48. private int finish_stringwidth(P1(os_ptr));
  49. private int op_show_cleanup(P1(os_ptr));
  50.  
  51. /* <string> show - */
  52. private int
  53. zshow(register os_ptr op)
  54. {    gs_show_enum *penum;
  55.     int code = op_show_setup(op, &penum);
  56.     if ( code != 0 )
  57.       return code;
  58.     if ( (code = gs_show_n_init(penum, igs, (char *)op->value.bytes, r_size(op))) < 0 )
  59.        {    ifree_object(penum, "op_show_enum_setup");
  60.         return code;
  61.        }
  62.     op_show_finish_setup(penum, 1, finish_show);
  63.     return op_show_continue(op - 1);
  64. }
  65.  
  66. /* <ax> <ay> <string> ashow - */
  67. private int
  68. zashow(register os_ptr op)
  69. {    gs_show_enum *penum;
  70.     int code;
  71.     float axy[2];
  72.     if (    (code = num_params(op - 1, 2, axy)) < 0 ||
  73.         (code = op_show_setup(op, &penum)) != 0
  74.        )
  75.         return code;
  76.     if ( (code = gs_ashow_n_init(penum, igs, axy[0], axy[1], (char *)op->value.bytes, r_size(op))) < 0 )
  77.        {    ifree_object(penum, "op_show_enum_setup");
  78.         return code;
  79.        }
  80.     op_show_finish_setup(penum, 3, finish_show);
  81.     return op_show_continue(op - 3);
  82. }
  83.  
  84. /* <cx> <cy> <char> <string> widthshow - */
  85. private int
  86. zwidthshow(register os_ptr op)
  87. {    gs_show_enum *penum;
  88.     int code;
  89.     float cxy[2];
  90.     check_type(op[-1], t_integer);
  91.     if ( (gs_char)(op[-1].value.intval) != op[-1].value.intval )
  92.         return_error(e_rangecheck);
  93.     if (    (code = num_params(op - 2, 2, cxy)) < 0 ||
  94.         (code = op_show_setup(op, &penum)) != 0
  95.        )
  96.         return code;
  97.     if ( (code = gs_widthshow_n_init(penum, igs, cxy[0], cxy[1],
  98.                      (gs_char)op[-1].value.intval,
  99.                      (char *)op->value.bytes,
  100.                      r_size(op))) < 0 )
  101.        {    ifree_object(penum, "op_show_enum_setup");
  102.         return code;
  103.        }
  104.     op_show_finish_setup(penum, 4, finish_show);
  105.     return op_show_continue(op - 4);
  106. }
  107.  
  108. /* <cx> <cy> <char> <ax> <ay> <string> awidthshow - */
  109. private int
  110. zawidthshow(register os_ptr op)
  111. {    gs_show_enum *penum;
  112.     int code;
  113.     float cxy[2], axy[2];
  114.     check_type(op[-3], t_integer);
  115.     if ( (gs_char)(op[-3].value.intval) != op[-3].value.intval )
  116.         return_error(e_rangecheck);
  117.     if (    (code = num_params(op - 4, 2, cxy)) < 0 ||
  118.         (code = num_params(op - 1, 2, axy)) < 0 ||
  119.         (code = op_show_setup(op, &penum)) != 0
  120.        )
  121.         return code;
  122.     if ( (code = gs_awidthshow_n_init(penum, igs, cxy[0], cxy[1],
  123.                       (gs_char)op[-3].value.intval,
  124.                       axy[0], axy[1],
  125.                       (char *)op->value.bytes,
  126.                       r_size(op))) < 0 )
  127.        {    ifree_object(penum, "op_show_enum_setup");
  128.         return code;
  129.        }
  130.     op_show_finish_setup(penum, 6, finish_show);
  131.     return op_show_continue(op - 6);
  132. }
  133.  
  134. /* <proc> <string> kshow - */
  135. private int
  136. zkshow(register os_ptr op)
  137. {    gs_show_enum *penum;
  138.     int code;
  139.     check_proc(op[-1]);
  140.     if ( (code = op_show_setup(op, &penum)) != 0 )
  141.       return code;
  142.     if ( (code = gs_kshow_n_init(penum, igs, (char *)op->value.bytes, r_size(op))) < 0 )
  143.        {    ifree_object(penum, "op_show_enum_setup");
  144.         return code;
  145.        }
  146.     op_show_finish_setup(penum, 2, finish_show);
  147.     sslot = op[-1];        /* save kerning proc */
  148.     return op_show_continue(op - 2);
  149. }
  150.  
  151. /* Common finish procedure for all show operations. */
  152. /* Doesn't have to do anything. */
  153. private int
  154. finish_show(os_ptr op)
  155. {    return 0;
  156. }
  157.  
  158. /* <string> stringwidth <wx> <wy> */
  159. private int
  160. zstringwidth(register os_ptr op)
  161. {    gs_show_enum *penum;
  162.     int code = op_show_setup(op, &penum);
  163.     if ( code != 0 )
  164.       return code;
  165.     if ( (code = gs_stringwidth_n_init(penum, igs, (char *)op->value.bytes, r_size(op))) < 0 )
  166.        {    ifree_object(penum, "op_show_enum_setup");
  167.         return code;
  168.        }
  169.     op_show_finish_setup(penum, 1, finish_stringwidth);
  170.     return op_show_continue(op - 1);
  171. }
  172. /* Finishing procedure for stringwidth. */
  173. /* Pushes the accumulated width. */
  174. private int
  175. finish_stringwidth(register os_ptr op)
  176. {    gs_point width;
  177.     gs_show_width(senum, &width);
  178.     push(2);
  179.     make_real(op - 1, width.x);
  180.     make_real(op, width.y);
  181.     return 0;
  182. }
  183.  
  184. /* Common code for charpath and .charboxpath. */
  185. private int
  186. zchar_path(register os_ptr op,
  187.   int (*init)(P5(gs_show_enum *, gs_state *, const char *, uint, bool)))
  188. {    gs_show_enum *penum;
  189.     int code;
  190.     check_type(*op, t_boolean);
  191.     code = op_show_setup(op - 1, &penum);
  192.     if ( code != 0 )
  193.       return code;
  194.     if ( (code = (*init)(penum, igs, (char *)op[-1].value.bytes, r_size(op - 1), op->value.boolval)) < 0 )
  195.        {    ifree_object(penum, "op_show_enum_setup");
  196.         return code;
  197.        }
  198.     op_show_finish_setup(penum, 2, finish_show);
  199.     return op_show_continue(op - 2);
  200. }
  201. /* <string> <outline_bool> charpath - */
  202. private int
  203. zcharpath(register os_ptr op)
  204. {    return zchar_path(op, gs_charpath_n_init);
  205. }
  206. /* <string> <box_bool> .charboxpath - */
  207. private int
  208. zcharboxpath(register os_ptr op)
  209. {    return zchar_path(op, gs_charboxpath_n_init);
  210. }
  211.  
  212. /* <wx> <wy> <llx> <lly> <urx> <ury> setcachedevice - */
  213. int
  214. zsetcachedevice(register os_ptr op)
  215. {    float wbox[6];
  216.     gs_show_enum *penum = op_show_find();
  217.     int code = num_params(op, 6, wbox);
  218.     if ( penum == 0 )
  219.       return_error(e_undefined);
  220.     if ( code < 0 )
  221.       return code;
  222.     if ( gs_show_width_only(penum) )
  223.       return op_show_return_width(op, 6, &wbox[0]);
  224.     code = gs_setcachedevice(penum, igs, wbox);
  225.     if ( code < 0 )
  226.       return code;
  227.     pop(6);
  228.     if ( code == 1 )
  229.       clear_pagedevice(istate);
  230.     return 0;
  231. }
  232.  
  233. /* <w0x> <w0y> <llx> <lly> <urx> <ury> <w1x> <w1y> <vx> <vy> setcachedevice2 - */
  234. int
  235. zsetcachedevice2(os_ptr op)
  236. {    float wbox[10];
  237.     gs_show_enum *penum = op_show_find();
  238.     int code = num_params(op, 10, wbox);
  239.     if ( penum == 0 )
  240.       return_error(e_undefined);
  241.     if ( code < 0 )
  242.       return code;
  243.     if ( gs_show_width_only(penum) )
  244.       return op_show_return_width(op, 10,
  245.                       (gs_rootfont(igs)->WMode ?
  246.                        &wbox[6] : &wbox[0]));
  247.     code = gs_setcachedevice2(penum, igs, wbox);
  248.     if ( code < 0 )
  249.       return code;
  250.     pop(10);
  251.     if ( code == 1 )
  252.       clear_pagedevice(istate);
  253.     return 0;
  254. }
  255.  
  256. /* <wx> <wy> setcharwidth - */
  257. private int
  258. zsetcharwidth(register os_ptr op)
  259. {    float width[2];
  260.     gs_show_enum *penum = op_show_find();
  261.     int code = num_params(op, 2, width);
  262.     if ( penum == 0 )
  263.       return_error(e_undefined);
  264.     if ( code < 0 )
  265.       return code;
  266.     if ( gs_show_width_only(penum) )
  267.       return op_show_return_width(op, 2, &width[0]);
  268.     code = gs_setcharwidth(penum, igs, width[0], width[1]);
  269.     if ( code < 0 )
  270.       return code;
  271.     pop(2);
  272.     return 0;
  273. }
  274.  
  275. /* <dict> .fontbbox <llx> <lly> <urx> <ury> -true- */
  276. /* <dict> .fontbbox -false- */
  277. private int
  278. zfontbbox(register os_ptr op)
  279. {    float bbox[4];
  280.     int code;
  281.  
  282.     check_type(*op, t_dictionary);
  283.     check_dict_read(*op);
  284.     code = font_bbox_param(op, bbox);
  285.     if ( code < 0 )
  286.       return code;
  287.     if ( bbox[0] < bbox[2] && bbox[1] < bbox[3] )
  288.     {    push(4);
  289.         make_reals(op - 4, bbox, 4);
  290.         make_true(op);
  291.     }
  292.     else
  293.     {    /* No bbox, or an empty one. */
  294.         make_false(op);
  295.     }
  296.     return 0;
  297. }
  298.  
  299. /* ------ Initialization procedure ------ */
  300.  
  301. BEGIN_OP_DEFS(zchar_op_defs) {
  302.     {"3ashow", zashow},
  303.     {"6awidthshow", zawidthshow},
  304.     {"2charpath", zcharpath},
  305.     {"2.charboxpath", zcharboxpath},
  306.     {"2kshow", zkshow},
  307.     {"6setcachedevice", zsetcachedevice},
  308.     {":setcachedevice2", zsetcachedevice2},
  309.     {"2setcharwidth", zsetcharwidth},
  310.     {"1show", zshow},
  311.     {"1stringwidth", zstringwidth},
  312.     {"4widthshow", zwidthshow},
  313.         /* Extensions */
  314.     {"1.fontbbox", zfontbbox},
  315.         /* Internal operators */
  316.     {"0%finish_show", finish_show},
  317.     {"0%finish_stringwidth", finish_stringwidth},
  318.     {"0%op_show_continue", op_show_continue},
  319. END_OP_DEFS(0) }
  320.  
  321. /* ------ Subroutines ------ */
  322.  
  323. /* Most of these are exported for zchar2.c. */ 
  324.  
  325. /* Convert a glyph to a ref. */
  326. private void
  327. glyph_ref(gs_glyph glyph, ref *gref)
  328. {    if ( glyph < gs_min_cid_glyph )
  329.       name_index_ref(glyph, gref);
  330.     else
  331.       make_int(gref, glyph - gs_min_cid_glyph);
  332. }
  333.  
  334. /* Prepare to set up for a show operator. */
  335. /* Don't change any state yet. */
  336. int
  337. op_show_setup(os_ptr op, gs_show_enum **ppenum)
  338. {    check_read_type(*op, t_string);
  339.     return op_show_enum_setup(op, ppenum);
  340. }
  341. int
  342. op_show_enum_setup(os_ptr op, gs_show_enum **ppenum)
  343. {    /* Provide a special "hook" for an unusual application */
  344.     /* that needs to be able to intervene before any operator */
  345.     /* that renders or measures characters. */
  346. #ifdef OP_SHOW_ENUM_SETUP_HOOK
  347.     OP_SHOW_ENUM_SETUP_HOOK
  348. #endif
  349.     check_estack(snumpush + 2);
  350.     if ( (*ppenum = gs_show_enum_alloc((gs_memory_t *)imemory, igs, "op_show_enum_setup")) == 0 )
  351.       return_error(e_VMerror);
  352.     return 0;
  353. }
  354.  
  355. /* Finish setting up a show operator.  This can't fail, since */
  356. /* op_show_enum_setup did the check_estack. */
  357. void
  358. op_show_finish_setup(gs_show_enum *penum, int npop, op_proc_p endproc /* end procedure */)
  359. {    register es_ptr ep = esp + snumpush;
  360.     esp = ep;
  361.     make_mark_estack(ep - (snumpush - 1), es_show, op_show_cleanup);
  362.     if ( endproc == NULL )
  363.       endproc = finish_show;
  364.     make_null(&esslot(ep));
  365.     make_int(&essindex(ep), 0);
  366.     make_int(&esodepth(ep), 0);    /* see gs_show_render case in */
  367.                     /* op_show_continue_dispatch */
  368.     make_int(&esddepth(ep), 0);    /* ditto */
  369.     make_int(&esgslevel(ep), igs->level);
  370.     make_op_estack(&eseproc(ep), endproc);
  371.     make_istruct(ep, 0, penum);
  372. }
  373.  
  374. /* Continuation operator for character rendering. */
  375. int
  376. op_show_continue(os_ptr op)
  377. {    return op_show_continue_dispatch(op, gs_show_next(senum));
  378. }
  379. /* Note that this sets osp = op explicitly iff the dispatch succeeds. */
  380. /* This is so that the show operators don't pop anything from the o-stack */
  381. /* if they don't succeed. */
  382. /* Note also that if it returns an error, it has freed the enumerator. */
  383. int
  384. op_show_continue_dispatch(register os_ptr op, int code)
  385. {    gs_show_enum *penum = senum;
  386.     switch ( code )
  387.     {
  388.     case 0:                /* all done */
  389.     {    os_ptr save_osp = osp;
  390.         osp = op;
  391.         code = (*real_opproc(&seproc))(op);
  392.         op_show_free(code);
  393.         if ( code < 0 )
  394.         {    osp = save_osp;
  395.             return code;
  396.         }
  397.         return o_pop_estack;
  398.     }
  399.     case gs_show_kern:
  400.     {    ref *pslot = &sslot;
  401.         push(2);
  402.         make_int(op - 1, gs_kshow_previous_char(penum));
  403.         make_int(op, gs_kshow_next_char(penum));
  404.         push_op_estack(op_show_continue);        /* continue after kerning */
  405.         *++esp = *pslot;    /* kerning procedure */
  406.     }
  407.         return o_push_estack;
  408.     case gs_show_render:
  409.     {    gs_font *pfont = gs_currentfont(igs);
  410.         font_data *pfdata = pfont_data(pfont);
  411.         gs_char chr = gs_show_current_char(penum);
  412.         gs_glyph glyph = gs_show_current_glyph(penum);
  413.         push(2);
  414.         op[-1] = pfdata->dict;    /* push the font */
  415.         /*
  416.          * For Type 1 and Type 4 fonts, prefer BuildChar to
  417.          * BuildGlyph, so that PostScript procedures appearing in the
  418.          * CharStrings dictionary will receive the character code
  419.          * rather than the character name; for Type 3 fonts,
  420.          * prefer BuildGlyph to BuildChar.
  421.          */
  422.         if ( pfont->FontType == ft_user_defined )
  423.         {    /* Type 3 font, prefer BuildGlyph. */
  424.             if ( level2_enabled &&
  425.                  !r_has_type(&pfdata->BuildGlyph, t_null) &&
  426.                  glyph != gs_no_glyph
  427.                )
  428.             {    glyph_ref(glyph, op);
  429.                 esp[2] = pfdata->BuildGlyph;
  430.             }
  431.             else if ( r_has_type(&pfdata->BuildChar, t_null) )
  432.                 goto err;
  433.             else if ( chr == gs_no_char )
  434.               {    /* glyphshow, reverse map the character */
  435.                 /* through the Encoding */
  436.                 ref gref;
  437.                 const ref *pencoding = &pfdata->Encoding;
  438.  
  439.                 glyph_ref(glyph, &gref);
  440.                 if ( !map_glyph_to_char(&gref, pencoding,
  441.                             (ref *)op)
  442.                    )
  443.                   {    /* Not found, try .notdef */
  444.                     name_enter_string(".notdef", &gref);
  445.                     if ( !map_glyph_to_char(&gref,
  446.                                 pencoding,
  447.                                 (ref *)op)
  448.                        )
  449.                       goto err;
  450.                   }
  451.                 esp[2] = pfdata->BuildChar;
  452.               }
  453.             else
  454.             {    make_int(op, chr);
  455.                 esp[2] = pfdata->BuildChar;
  456.             }
  457.         }
  458.         else
  459.         {    /* Type 1 or Type 4 font, prefer BuildChar. */
  460.             /* We know that both BuildChar and BuildGlyph */
  461.             /* are present. */
  462.             if ( chr != gs_no_char )
  463.             {    make_int(op, chr);
  464.                 esp[2] = pfdata->BuildChar;
  465.             }
  466.             else
  467.             {    glyph_ref(glyph, op);
  468.                 esp[2] = pfdata->BuildGlyph;
  469.             }
  470.         }
  471.         /* Save the stack depths in case we bail out. */
  472.         sodepth.value.intval = ref_stack_count(&o_stack) - 2;
  473.         sddepth.value.intval = ref_stack_count(&d_stack);
  474.         push_op_estack(op_show_continue);
  475.         ++esp;        /* skip BuildChar or BuildGlyph proc */
  476.     }
  477.         return o_push_estack;
  478.     default:            /* error */
  479. err:        if ( code >= 0 )
  480.           code = gs_note_error(e_invalidfont);
  481.         return op_show_free(code);
  482.     }
  483. }
  484. /* Reverse-map a glyph name to a character code for glyphshow. */
  485. private bool
  486. map_glyph_to_char(const ref *pgref, const ref *pencoding, ref *pch)
  487. {    uint esize = r_size(pencoding);
  488.     uint ch;
  489.     ref eref;
  490.     for ( ch = 0; ch < esize; ch++ )
  491.       {    array_get(pencoding, (long)ch, &eref);
  492.         if ( obj_eq(pgref, &eref) )
  493.           { make_int(pch, ch);
  494.             return true;
  495.           }
  496.       }
  497.     return false;
  498. }
  499.  
  500. /* Find the index of the e-stack mark for the current show enumerator. */
  501. /* Return 0 if we can't find the mark. */
  502. uint
  503. op_show_find_index(void)
  504. {    uint count = 0;
  505.     STACK_LOOP_BEGIN(&e_stack, ep, size)
  506.       for ( ep += size - 1; size != 0; size--, ep--, count++ )
  507.         if ( r_is_estack_mark(ep) && estack_mark_index(ep) == es_show )
  508.           return count;
  509.     STACK_LOOP_END(ep, size)
  510.     return 0;            /* no mark */
  511. }
  512.  
  513. /* Find the current show enumerator on the e-stack. */
  514. gs_show_enum *
  515. op_show_find(void)
  516. {    uint index = op_show_find_index();
  517.     if ( index == 0 )
  518.       return 0;            /* no mark */
  519.     return r_ptr(ref_stack_index(&e_stack, index - (snumpush - 1)),
  520.              gs_show_enum);
  521. }
  522.  
  523. /* Shortcut the BuildChar or BuildGlyph procedure at the point */
  524. /* of the setcharwidth or the setcachedevice[2] if we are in */
  525. /* a stringwidth or cshow, or if we are only collecting the scalable */
  526. /* width for an xfont character. */
  527. int
  528. op_show_return_width(os_ptr op, uint npop, float *pwidth)
  529. {    uint index = op_show_find_index();
  530.     es_ptr ep = (es_ptr)ref_stack_index(&e_stack, index - (snumpush - 1));
  531.     int code = gs_setcharwidth(esenum(ep), igs, pwidth[0], pwidth[1]);
  532.     uint ocount, dsaved, dcount;
  533.  
  534.     if ( code < 0 )
  535.       return code;
  536.     /* Restore the operand and dictionary stacks. */
  537.     ocount = ref_stack_count(&o_stack) - (uint)esodepth(ep).value.intval;
  538.     if ( ocount < npop )
  539.       return_error(e_stackunderflow);
  540.     dsaved = (uint)esddepth(ep).value.intval;
  541.     dcount = ref_stack_count(&d_stack);
  542.     if ( dcount < dsaved )
  543.       return_error(e_dictstackunderflow);
  544.     while ( dcount > dsaved )
  545.       {    code = zend(op);
  546.         if ( code < 0 )
  547.           return code;
  548.         dcount--;
  549.       }
  550.     ref_stack_pop(&o_stack, ocount);
  551.     /* We don't want to pop the mark or the continuation */
  552.     /* procedure (op_show_continue or cshow_continue). */
  553.     pop_estack(index - snumpush);
  554.     return o_pop_estack;
  555. }
  556.  
  557. /*
  558.  * Restore state after finishing, or unwinding from an error within, a show
  559.  * operation.  Note that we assume op == osp, and may reset osp.
  560.  */
  561. private int
  562. op_show_restore(bool for_error)
  563. {    register es_ptr ep = esp + snumpush;
  564.     gs_show_enum *penum = esenum(ep);
  565.     int saved_level = esgslevel(ep).value.intval;
  566.     int code = 0;
  567.  
  568.     if ( for_error )
  569.       { uint saved_count = esodepth(ep).value.intval;
  570.         uint count = ref_stack_count(&o_stack);
  571.  
  572.         if ( count > saved_count ) /* if <, we're in trouble */
  573.           ref_stack_pop(&o_stack, count - saved_count);
  574.       }
  575.     if ( penum->stringwidth_flag == 1 )
  576.       { /* stringwidth does an extra gsave */
  577.         --saved_level;
  578.       }
  579.     while ( igs->level > saved_level && code >= 0 )
  580.       { if ( igs->saved == 0 || igs->saved->saved == 0 )
  581.           { /*
  582.          * Bad news: we got an error inside a save inside a
  583.          * BuildChar or BuildGlyph.  Don't attempt to recover.
  584.          */
  585.         code = gs_note_error(e_Fatal);
  586.           }
  587.         else
  588.           code = gs_grestore(igs);
  589.       }
  590.     gs_show_enum_release(penum, (gs_memory_t *)imemory);
  591.     return code;
  592. }
  593. /* Clean up after an error. */
  594. private int
  595. op_show_cleanup(os_ptr op)
  596. {    return op_show_restore(true);
  597. }
  598. /* Clean up after termination of a show operation. */
  599. int
  600. op_show_free(int code)
  601. {    int rcode;
  602.     esp -= snumpush;
  603.     rcode = op_show_restore(code < 0);
  604.     return (rcode < 0 ? rcode : code);
  605. }
  606.  
  607. /* Get a FontBBox parameter from a font dictionary. */
  608. int
  609. font_bbox_param(const ref *pfdict, float bbox[4])
  610. {    ref *pbbox;
  611.     /*
  612.      * Pre-clear the bbox in case it's invalid.  The Red Books say that
  613.      * FontBBox is required, but the Adobe interpreters don't require
  614.      * it, and a few user-written fonts don't supply it, or supply one
  615.      * of the wrong size (!); also, PageMaker 5.0 (an Adobe product!)
  616.      * sometimes emits an absurd bbox for Type 1 fonts converted from
  617.      * TrueType.
  618.      */
  619.     bbox[0] = bbox[1] = bbox[2] = bbox[3] = 0.0;
  620.     if ( dict_find_string(pfdict, "FontBBox", &pbbox) > 0 )
  621.     {    if ( !r_is_array(pbbox) )
  622.           return_error(e_typecheck);
  623.         if ( r_size(pbbox) == 4 )
  624.         {    const ref_packed *pbe = pbbox->value.packed;
  625.             ref rbe[4];
  626.             int i;
  627.             int code;
  628.             float dx, dy, ratio;
  629.  
  630.             for ( i = 0; i < 4; i++ )
  631.               {    packed_get(pbe, rbe + i);
  632.                 pbe = packed_next(pbe);
  633.               }
  634.             if ( (code = num_params(rbe + 3, 4, bbox)) < 0 )
  635.               return code;
  636.             /* Require "reasonable" values.  Thanks to Ray */
  637.             /* Johnston for suggesting the following test. */
  638.             dx = bbox[2] - bbox[0];
  639.             dy = bbox[3] - bbox[1];
  640.             if ( dx <= 0 || dy <= 0 ||
  641.                  (ratio = dy / dx) < 0.125 || ratio > 8.0
  642.                )
  643.               bbox[0] = bbox[1] = bbox[2] = bbox[3] = 0.0;
  644.         }
  645.     }
  646.     return 0;
  647. }
  648.